package cn.tedu.csmall.passport.ex.handler;

import cn.tedu.csmall.passport.ex.ServiceException;
import cn.tedu.csmall.passport.web.JsonResult;
import cn.tedu.csmall.passport.web.ServiceCode;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 登陆成功后账号无权限时
     * @param e
     * @return
     */
    @ExceptionHandler
    public JsonResult handleAccessDeniedException(AccessDeniedException e) {
        log.warn("程序运行过程中出现了AccessDeniedException，将统一处理！");
        log.warn("异常：", e);
        return JsonResult.error(ServiceCode.ERR_FORBIDDEN, "您当前登陆的账号无此权限，禁止访问！");
    }

    /**
     * 使用security框架登录处理时账号被禁用
     * @param e
     * @return
     */
    @ExceptionHandler
    public JsonResult handleDisabledException(DisabledException e) {
        log.warn("程序运行过程中出现了DisabledException，将统一处理！");
        log.warn("异常：", e);
        return JsonResult.error(ServiceCode.ERR_UNAUTHORIZED_DISABLE, "账号被禁用！");
    }

    /**
     * 使用security框架登录处理时用户名或密码错误
     * @param e
     * @return
     */
    // 如果 @ExceptionHandler 没有配置参数，则以方法参数的异常为准，来处理异常
    // 如果 @ExceptionHandler 配置了参数，则只处理此处配置的异常
    @ExceptionHandler({
            InternalAuthenticationServiceException.class,
            BadCredentialsException.class
    })
    public JsonResult handleAuthenticationException(AuthenticationException e) {
        log.warn("程序运行过程中出现了AuthenticationException，将统一处理！");
        log.warn("异常：", e);
        return JsonResult.error(ServiceCode.ERR_UNAUTHORIZED, "用户名或密码错误！");
    }

    @ExceptionHandler
    public JsonResult handlerMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.warn("程序运行过程中出现ServiceException，将统一处理");
        log.warn("异常信息：{}",e.getMessage());
        return JsonResult.error(ServiceCode.ERR_BAD_REQUEST, e.getMessage());
    }

    @ExceptionHandler
    public JsonResult handlerConstraintViolationException(ConstraintViolationException e) {
        log.warn("程序运行过程中出现ConstraintViolationException，将统一处理");
        log.warn("异常信息：{}",e.getMessage());
        String message = null;
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        for (ConstraintViolation<?> constraintViolation : constraintViolations) {
            message = constraintViolation.getMessage();
        }
        // 未封装参数出错
        return JsonResult.error(ServiceCode.ERR_BAD_REQUEST,message);
    }

    @ExceptionHandler
    public JsonResult handlerBindException(BindException e) {
        log.warn("程序运行过程中出现BindException，将统一处理");
        log.warn("异常信息：{}",e.getMessage());

        // 返回多个错误信息的任意一种
        String message = e.getFieldError().getDefaultMessage();
        return JsonResult.error(ServiceCode.ERR_BAD_REQUEST,message);
    }

    @ExceptionHandler
    public JsonResult handlerServiceException(ServiceException e) {
        log.warn("程序运行过程中出现ServiceException，将统一处理");
        log.warn("异常信息：{}",e.getMessage());
        return JsonResult.error(e.getServiceCode(), e.getMessage());
    }

    @ExceptionHandler
    public JsonResult handleThrowable(Throwable e) {
        log.warn("程序运行过程中出现了Throwable，将统一处理！");
        log.warn("异常：", e); // 取代 e.printStackTrace();，效果相同，注意，第1个参数中不要使用 {} 进行占位
        return JsonResult.error(ServiceCode.ERR_UNKNOWN);
    }

}

